#include "MatrixUtilsTest.h"

#include <cassert>
#include <string>

#include "MatrixUtils.h"

namespace UnitTests
{
    void MatrixUtilsTests::AddMatricesTest() const
    {
    	// Matrices sizes
    	static const uint32_t rows = 10;
    	static const uint32_t columns = 15;
    	static const uint32_t numberOfElements = rows * columns;

    	// Create matrices to add.
    	Utils::Matriz first(rows, columns);
    	Utils::Matriz second(rows, columns);
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		const double value = static_cast<double>(i);
    		first.mData[i] = -value;
    		second.mData[i] = value;
    	}
    	
    	// Add matrices
    	Utils::Matriz result = Utils::AddMatrices(first, second);
    	
    	// Check that the result is correct.
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		assert(result.mData[i] == 0.0 && "AddMatricesTest: The sum of matrices is incorrect.");
    	}
    }

    void MatrixUtilsTests::SubtractMatricesTest() const
    {
    	// Matrices sizes
    	static const uint32_t rows = 7;
    	static const uint32_t columns = 7;
    	static const uint32_t numberOfElements = rows * columns;

    	// Create matrices to add.
    	Utils::Matriz first(rows, columns);
    	Utils::Matriz second(rows, columns);
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		const double value = static_cast<double>(i);
    		first.mData[i] = value;
    		second.mData[i] = value;
    	}
    	
    	// Subtract matrices
    	Utils::Matriz result = Utils::SubtractMatrices(first, second);

    	// Check that the result is correct.
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		assert(result.mData[i] == 0.0 && "AddMatricesTest: The subtraction of matrices is incorrect.");
    	}
    }

    void MatrixUtilsTests::MultiplyMatricesTest() const
    {
    	// Matrices sizes
    	static const uint32_t firstRows = 3;
    	static const uint32_t firstColumns = 3;
    	static const uint32_t secondRows = firstColumns;
    	static const uint32_t secondColumns = 4;

    	// Create matrices to multiply
    	Utils::Matriz first(firstRows, firstColumns);
    	Utils::Matriz second(secondRows, secondColumns);
    	const uint32_t firstNumberOfElements = firstRows * firstColumns;
    	// Identity matrix
    	memset(first.mData, 0, firstNumberOfElements * sizeof(double));
    	for(uint32_t i = 0; i < firstNumberOfElements; i += first.mColumns + 1)
    	{
    		first.mData[i] = 1.0;
    	}
    	
    	for(uint32_t i = 0; i < secondRows * secondColumns; ++i)
    	{
    		const double value = static_cast<double>(i);
    		second.mData[i] = value; 
    	}
    	
    	// Multiply matrices and check result.
    	Utils::Matriz result = Utils::MultiplyMatrices(first, second);
    	for(uint32_t i = 0; i < result.mRows * result.mColumns; ++i)
    	{
    		assert(second.mData[i] == result.mData[i] && "MultiplyMatricesTest: The matrices must be equals.");
    	}
    }

    void MatrixUtilsTests::MultiplyMatrixByLiteralTest() const
    {
    	// Matrix sizes
    	static const uint32_t rows = 5;
    	static const uint32_t columns = 9;
    	static const double lit = -1.0;

    	// Init matrix
    	Utils::Matriz m(rows, columns);
    	const uint32_t numberOfElements = rows * columns;
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		m.mData[i] = i;
    	}

    	// Multiply matrix by a literal and check the result.
    	Utils::Matriz result = Utils::MultiplyMatrixByLiteral(m, lit);

    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		assert(result.mData[i] == -m.mData[i] && "MultiplyMatrixByLiteralTest: The matrices are not the negative of the other.");
    	}
    }

    void MatrixUtilsTests::GetMatrixFromFileTest()
    {
    	// Filepath and matrix sizes
    	static const std::string filePath = "testFiles/matrix.txt";
    	static const uint32_t rows = 3;
    	static const uint32_t columns = 4;

    	// Get the matrix from file and check results.
    	Utils::Matriz result = Utils::GetMatrixFromFile(filePath, rows, columns);

    	assert(result.mData[0] == 4.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[1] == -1.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[2] == 0.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[3] == -1.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[4] == -1.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[5] == 4.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[6] == -1.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[7] == 0.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[8] == 0.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[9] == -1.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[10] == 4.0 && "GetMatrixFromFileTest: Incorrect read value.");
    	assert(result.mData[11] == 0.0 && "GetMatrixFromFileTest: Incorrect read value.");
    }

    void MatrixUtilsTests::GetAbsoluteMatrixTest() const
    {
    	// Matrix data
    	static const uint32_t rows = 7;
    	static const uint32_t columns = 8;
    	static const double numberOfElements = rows * columns;

    	// Init the matrix.
    	Utils::Matriz matrix(rows, columns);
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		const double value = static_cast<double>(i);
    		matrix.mData[i] = -value;
    	}

    	// Get the absolute matrix and check results
    	Utils::Matriz result = Utils::GetAbsoluteMatrix(matrix);
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		assert(result.mData[i] == -matrix.mData[i] 
    		    && "GetAbsoluteMatrixTest: The result matrix is not the absolute value matrix");
    	}
    }

    void MatrixUtilsTests::GetMaxElementTest() const
    {
    	// Matrix data
    	static const uint32_t rows = 7;
    	static const uint32_t columns = 8;
    	static const double numberOfElements = rows * columns;

    	// Init the matrix.
    	Utils::Matriz matrix(rows, columns);
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		const double value = static_cast<double>(i);
    		matrix.mData[i] = value;
    	}

    	const double result = Utils::GetMaxElement(matrix);
    	assert(result == numberOfElements - 1 && "GetMaxElementTest: That number is not the max element of the matrix.");
    }

    void MatrixUtilsTests::GetInfiniteVectorNormTest() const
    {
    	// Matrix data
    	static const uint32_t rows = 1;
    	static const uint32_t columns = 8;
    	static const double numberOfElements = rows * columns;

    	// Init the matrix.
    	Utils::Matriz matrix(rows, columns);
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		const double value = static_cast<double>(i);
    		matrix.mData[i] = -value;
    	}

    	const double result = Utils::GetInfiniteVectorNorm(matrix);
    	assert(result == numberOfElements - 1 && "GetInfiniteVectorNormTest: That number is not the infinite vector norm");
    }

    void MatrixUtilsTests::GetFirstOrderVectorNormTest() const
    {
    	// Matrix data
    	static const uint32_t rows = 1;
    	static const uint32_t columns = 8;
    	static const double numberOfElements = rows * columns;

    	// Init the matrix.
    	Utils::Matriz matrix(rows, columns);
    	double sumOfAbsoluteMembers = 0.0;
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		const double value = -static_cast<double>(i);
    		matrix.mData[i] = value;
    		sumOfAbsoluteMembers += (0.0 <= value) ? value : -value;
    	}

    	const double result = Utils::GetFirstOrderVectorNorm(matrix);
    	assert(sumOfAbsoluteMembers == result && "GetFirstOrderVectorNormTest: Incorrect value");
    }

    void MatrixUtilsTests::AddMatrixElementsTest() const
    {
    	static const uint32_t rows = 7;
    	static const uint32_t columns = 8;
    	static const double numberOfElements = rows * columns;

    	// Init the matrix.
    	Utils::Matriz matrix(rows, columns);
    	double sumOfMembers = 0.0;
    	for(uint32_t i = 0; i < numberOfElements; ++i)
    	{
    		const double value = -static_cast<double>(i);
    		matrix.mData[i] = value;
    		sumOfMembers += value;
    	}

    	// Compute sum of elements of the matrix and check the result.
    	const double result = Utils::AddMatrixElements(matrix);
    	assert(result == sumOfMembers && "AddMatrixElementsTest: The sum is incorrect.");
    }
}
